home *** CD-ROM | disk | FTP | other *** search
/ Sprite 1984 - 1993 / Sprite 1984 - 1993.iso / src / lib / tk2.3 / dist / tkPack.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-02-29  |  29.8 KB  |  1,054 lines

  1. /* 
  2.  * tkPack.c --
  3.  *
  4.  *    This file contains code to implement the "packer"
  5.  *    geometry manager for Tk.
  6.  *
  7.  * Copyright 1990 Regents of the University of California.
  8.  * Permission to use, copy, modify, and distribute this
  9.  * software and its documentation for any purpose and without
  10.  * fee is hereby granted, provided that the above copyright
  11.  * notice appear in all copies.  The University of California
  12.  * makes no representations about the suitability of this
  13.  * software for any purpose.  It is provided "as is" without
  14.  * express or implied warranty.
  15.  */
  16.  
  17. #ifndef lint
  18. static char rcsid[] = "$Header: /user6/ouster/wish/RCS/tkPack.c,v 1.27 92/01/04 15:16:41 ouster Exp $ SPRITE (Berkeley)";
  19. #endif
  20.  
  21. #include "tkConfig.h"
  22. #include "tkInt.h"
  23.  
  24. typedef enum {TOP, BOTTOM, LEFT, RIGHT} Side;
  25.  
  26. /* For each window that the packer cares about (either because
  27.  * the window is managed by the packer or because the window
  28.  * has children that are managed by the packer), there is a
  29.  * structure of the following type:
  30.  */
  31.  
  32. typedef struct Packer {
  33.     Tk_Window tkwin;        /* Tk token for window.  NULL means that
  34.                  * the window has been deleted, but the
  35.                  * packet hasn't had a chance to clean up
  36.                  * yet because the structure is still in
  37.                  * use. */
  38.     struct Packer *parentPtr;    /* Parent within which this window
  39.                  * is packed (NULL means this window
  40.                  * isn't managed by the packer). */
  41.     struct Packer *nextPtr;    /* Next window packed within same
  42.                  * parent.  List is priority-ordered:
  43.                  * first on list gets packed first. */
  44.     struct Packer *childPtr;    /* First in list of children packed
  45.                  * inside this window (NULL means
  46.                  * no packed children). */
  47.     Side side;            /* Side of parent against which
  48.                  * this window is packed. */
  49.     Tk_Anchor anchorPoint;    /* If frame allocated for window is larger
  50.                  * than window needs, this indicates how
  51.                  * where to position window in frame. */
  52.     int padX, padY;        /* Additional amounts of space to give window
  53.                  * besides what it asked for. */
  54.     int doubleBw;        /* Twice the window's last known border
  55.                  * width.  If this changes, the window
  56.                  * must be repacked within its parent. */
  57.     int *abortPtr;        /* If non-NULL, it means that there is a nested
  58.                  * call to ArrangePacking already working on
  59.                  * this window.  *abortPtr may be set to 1 to
  60.                  * abort that nested call.  This happens, for
  61.                  * example, if tkwin or any of its children
  62.                  * is deleted. */
  63.     int flags;            /* Miscellaneous flags;  see below
  64.                  * for definitions. */
  65. } Packer;
  66.  
  67. /*
  68.  * Flag values for Packer structures:
  69.  *
  70.  * REQUESTED_REPACK:        1 means a Tk_DoWhenIdle request
  71.  *                has already been made to repack
  72.  *                all the children of this window.
  73.  * FILLX:            1 means if frame allocated for window
  74.  *                is wider than window needs, expand window
  75.  *                to fill frame.  0 means don't make window
  76.  *                any larger than needed.
  77.  * FILLY:            Same as FILLX, except for height.
  78.  * EXPAND:            1 means this window's frame will absorb any
  79.  *                extra space in the parent window.
  80.  */
  81.  
  82. #define REQUESTED_REPACK    1
  83. #define FILLX            2
  84. #define FILLY            4
  85. #define EXPAND            8
  86.  
  87. /*
  88.  * Hash table used to map from Tk_Window tokens to corresponding
  89.  * Packer structures:
  90.  */
  91.  
  92. static Tcl_HashTable packerHashTable;
  93.  
  94. /*
  95.  * Have statics in this module been initialized?
  96.  */
  97.  
  98. static initialized = 0;
  99.  
  100. /*
  101.  * Forward declarations for procedures defined later in this file:
  102.  */
  103.  
  104. static void        ArrangePacking _ANSI_ARGS_((ClientData clientData));
  105. static Packer *        GetPacker _ANSI_ARGS_((Tk_Window tkwin));
  106. static int        PackAfter _ANSI_ARGS_((Tcl_Interp *interp,
  107.                 Packer *prevPtr, Packer *parentPtr, int argc,
  108.                 char **argv));
  109. static void        PackReqProc _ANSI_ARGS_((ClientData clientData,
  110.                 Tk_Window tkwin));
  111. static void        PackStructureProc _ANSI_ARGS_((ClientData clientData,
  112.                 XEvent *eventPtr));
  113. static void        Unlink _ANSI_ARGS_((Packer *packPtr));
  114.  
  115. /*
  116.  *--------------------------------------------------------------
  117.  *
  118.  * Tk_PackCmd --
  119.  *
  120.  *    This procedure is invoked to process the "pack" Tcl command.
  121.  *    See the user documentation for details on what it does.
  122.  *
  123.  * Results:
  124.  *    A standard Tcl result.
  125.  *
  126.  * Side effects:
  127.  *    See the user documentation.
  128.  *
  129.  *--------------------------------------------------------------
  130.  */
  131.  
  132. int
  133. Tk_PackCmd(clientData, interp, argc, argv)
  134.     ClientData clientData;    /* Main window associated with
  135.                  * interpreter. */
  136.     Tcl_Interp *interp;        /* Current interpreter. */
  137.     int argc;            /* Number of arguments. */
  138.     char **argv;        /* Argument strings. */
  139. {
  140.     Tk_Window tkwin = (Tk_Window) clientData;
  141.     int length;
  142.     char c;
  143.  
  144.     if (argc < 3) {
  145.     Tcl_AppendResult(interp, "wrong # args: should be \"",
  146.         argv[0], " option arg ?arg ...?\"", (char *) NULL);
  147.     return TCL_ERROR;
  148.     }
  149.     c = argv[1][0];
  150.     length = strlen(argv[1]);
  151.     if ((c == 'a') && (length >= 2)
  152.         && (strncmp(argv[1], "after", length) == 0)) {
  153.     Packer *prevPtr;
  154.     Tk_Window tkwin2;
  155.  
  156.     tkwin2 = Tk_NameToWindow(interp, argv[2], tkwin);
  157.     if (tkwin2 == NULL) {
  158.         return TCL_ERROR;
  159.     }
  160.     prevPtr = GetPacker(tkwin2);
  161.     if (prevPtr->parentPtr == NULL) {
  162.         Tcl_AppendResult(interp, "window \"", argv[2],
  163.             "\" isn't packed", (char *) NULL);
  164.         return TCL_ERROR;
  165.     }
  166.     return PackAfter(interp, prevPtr, prevPtr->parentPtr, argc-3, argv+3);
  167.     } else if ((c == 'a') && (length >= 2)
  168.         && (strncmp(argv[1], "append", length) == 0)) {
  169.     Packer *parentPtr;
  170.     register Packer *prevPtr;
  171.     Tk_Window tkwin2;
  172.  
  173.     tkwin2 = Tk_NameToWindow(interp, argv[2], tkwin);
  174.     if (tkwin2 == NULL) {
  175.         return TCL_ERROR;
  176.     }
  177.     parentPtr = GetPacker(tkwin2);
  178.     prevPtr = parentPtr->childPtr;
  179.     if (prevPtr != NULL) {
  180.         while (prevPtr->nextPtr != NULL) {
  181.         prevPtr = prevPtr->nextPtr;
  182.         }
  183.     }
  184.     return PackAfter(interp, prevPtr, parentPtr, argc-3, argv+3);
  185.     } else if ((c == 'b') && (strncmp(argv[1], "before", length) == 0)) {
  186.     Packer *packPtr, *parentPtr;
  187.     register Packer *prevPtr;
  188.     Tk_Window tkwin2;
  189.  
  190.     tkwin2 = Tk_NameToWindow(interp, argv[2], tkwin);
  191.     if (tkwin2 == NULL) {
  192.         return TCL_ERROR;
  193.     }
  194.     packPtr = GetPacker(tkwin2);
  195.     if (packPtr->parentPtr == NULL) {
  196.         Tcl_AppendResult(interp, "window \"", argv[2],
  197.             "\" isn't packed", (char *) NULL);
  198.         return TCL_ERROR;
  199.     }
  200.     parentPtr = packPtr->parentPtr;
  201.     prevPtr = parentPtr->childPtr;
  202.     if (prevPtr == packPtr) {
  203.         prevPtr = NULL;
  204.     } else {
  205.         for ( ; ; prevPtr = prevPtr->nextPtr) {
  206.         if (prevPtr == NULL) {
  207.             panic("\"pack before\" couldn't find predecessor");
  208.         }
  209.         if (prevPtr->nextPtr == packPtr) {
  210.             break;
  211.         }
  212.         }
  213.     }
  214.     return PackAfter(interp, prevPtr, parentPtr, argc-3, argv+3);
  215.     } else if ((c == 'i') && (strncmp(argv[1], "info", length) == 0)) {
  216.     char *prefix;
  217.     register Packer *packPtr;
  218.     Tk_Window tkwin2;
  219.     char tmp[20];
  220.     static char *sideNames[] = {"top", "bottom", "left", "right"};
  221.  
  222.     if (argc != 3) {
  223.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  224.             argv[0], " info window\"", (char *) NULL);
  225.         return TCL_ERROR;
  226.     }
  227.     tkwin2 = Tk_NameToWindow(interp, argv[2], tkwin);
  228.     if (tkwin2 == NULL) {
  229.         return TCL_ERROR;
  230.     }
  231.     packPtr = GetPacker(tkwin2);
  232.     prefix = "";
  233.     for (packPtr = packPtr->childPtr; packPtr != NULL;
  234.         packPtr = packPtr->nextPtr) {
  235.         Tcl_AppendResult(interp, prefix, Tk_PathName(packPtr->tkwin),
  236.             " {", sideNames[(int) packPtr->side],
  237.             " frame ", Tk_NameOfAnchor(packPtr->anchorPoint),
  238.             (char *) NULL);
  239.         if (packPtr->padX != 0) {
  240.         sprintf(tmp, "%d", packPtr->padX);
  241.         Tcl_AppendResult(interp, " padx ", tmp, (char *) NULL);
  242.         }
  243.         if (packPtr->padY != 0) {
  244.         sprintf(tmp, "%d", packPtr->padY);
  245.         Tcl_AppendResult(interp, " pady ", tmp, (char *) NULL);
  246.         }
  247.         if (packPtr->flags & EXPAND) {
  248.         Tcl_AppendResult(interp, " expand", (char *) NULL);
  249.         }
  250.         if ((packPtr->flags & (FILLX|FILLY)) == (FILLX|FILLY)) {
  251.         Tcl_AppendResult(interp, " fill", (char *) NULL);
  252.         } else if (packPtr->flags & FILLX) {
  253.         Tcl_AppendResult(interp, " fillx", (char *) NULL);
  254.         } else if (packPtr->flags & FILLY) {
  255.         Tcl_AppendResult(interp, " filly", (char *) NULL);
  256.         }
  257.         Tcl_AppendResult(interp, "}", (char *) NULL);
  258.         prefix = " ";
  259.     }
  260.     return TCL_OK;
  261.     } else if ((c == 'u') && (strncmp(argv[1], "unpack", length) == 0)) {
  262.     Tk_Window tkwin2;
  263.     Packer *packPtr;
  264.  
  265.     if (argc != 3) {
  266.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  267.             argv[0], " unpack window\"", (char *) NULL);
  268.         return TCL_ERROR;
  269.     }
  270.     tkwin2 = Tk_NameToWindow(interp, argv[2], tkwin);
  271.     if (tkwin2 == NULL) {
  272.         return TCL_ERROR;
  273.     }
  274.     packPtr = GetPacker(tkwin2);
  275.     if ((packPtr != NULL) && (packPtr->parentPtr != NULL)) {
  276.         Tk_ManageGeometry(tkwin2, (Tk_GeometryProc *) NULL,
  277.             (ClientData) NULL);
  278.         Unlink(packPtr);
  279.         Tk_UnmapWindow(packPtr->tkwin);
  280.     }
  281.     } else {
  282.     Tcl_AppendResult(interp, "bad option \"", argv[1],
  283.         "\":  must be after, append, before, or info", (char *) NULL);
  284.     return TCL_ERROR;
  285.     }
  286.     return TCL_OK;
  287. }
  288.  
  289. /*
  290.  *--------------------------------------------------------------
  291.  *
  292.  * PackReqProc --
  293.  *
  294.  *    This procedure is invoked by Tk_GeometryRequest for
  295.  *    windows managed by the packer.
  296.  *
  297.  * Results:
  298.  *    None.
  299.  *
  300.  * Side effects:
  301.  *    Arranges for tkwin, and all its managed siblings, to
  302.  *    be re-packed at the next idle point.
  303.  *
  304.  *--------------------------------------------------------------
  305.  */
  306.  
  307.     /* ARGSUSED */
  308. static void
  309. PackReqProc(clientData, tkwin)
  310.     ClientData clientData;    /* Packer's information about
  311.                  * window that got new preferred
  312.                  * geometry.  */
  313.     Tk_Window tkwin;        /* Other Tk-related information
  314.                  * about the window. */
  315. {
  316.     register Packer *packPtr = (Packer *) clientData;
  317.  
  318.     packPtr = packPtr->parentPtr;
  319.     if (!(packPtr->flags & REQUESTED_REPACK)) {
  320.     packPtr->flags |= REQUESTED_REPACK;
  321.     Tk_DoWhenIdle(ArrangePacking, (ClientData) packPtr);
  322.     }
  323. }
  324.  
  325. /*
  326.  *--------------------------------------------------------------
  327.  *
  328.  * ArrangePacking --
  329.  *
  330.  *    This procedure is invoked (using the Tk_DoWhenIdle
  331.  *    mechanism) to re-layout a set of windows managed by
  332.  *    the packer.  It is invoked at idle time so that a
  333.  *    series of packer requests can be merged into a single
  334.  *    layout operation.
  335.  *
  336.  * Results:
  337.  *    None.
  338.  *
  339.  * Side effects:
  340.  *    The packed children of parentPtr may get resized or
  341.  *    moved.
  342.  *
  343.  *--------------------------------------------------------------
  344.  */
  345.  
  346. static void
  347. ArrangePacking(clientData)
  348.     ClientData clientData;    /* Structure describing parent
  349.                  * whose children are to be
  350.                  * re-layed out. */
  351. {
  352.     register Packer *parentPtr = (Packer *) clientData;
  353.     register Packer *childPtr;    
  354.     int numExpX, numExpY;    /* # of windows that are expandable in
  355.                  * each direction. */
  356.     int spareX, spareY;        /* Amount of extra space to give to each
  357.                  * expandable window. */
  358.     int leftOverX, leftOverY;    /* Extra chunk of space to give to last
  359.                  * expandable window. */
  360.     int cavityX, cavityY, cavityWidth, cavityHeight;
  361.                 /* These variables keep track of the
  362.                  * as-yet-unallocated space remaining in
  363.                  * the middle of the parent window. */
  364.     int frameX, frameY, frameWidth, frameHeight;
  365.                 /* These variables keep track of the frame
  366.                  * allocated to the current window. */
  367.     int x, y, width, height;    /* These variables are used to hold the
  368.                  * actual geometry of the current window. */
  369.     int intBWidth;        /* Width of internal border in parent window,
  370.                  * if any. */
  371.     int abort;            /* May get set to non-zero to abort this
  372.                  * repacking operation. */
  373.     int maxWidth, maxHeight, tmp;
  374.  
  375.     parentPtr->flags &= ~REQUESTED_REPACK;
  376.  
  377.     /*
  378.      * If the parent has no children anymore, then don't do anything
  379.      * at all:  just leave the parent's size as-is.
  380.      */
  381.  
  382.     if (parentPtr->childPtr == NULL) {
  383.     return;
  384.     }
  385.  
  386.     /*
  387.      * Abort any nested call to ArrangePacking for this window, since
  388.      * we'll do everything necessary here, and set up so this call
  389.      * can be aborted if necessary.  
  390.      */
  391.  
  392.     if (parentPtr->abortPtr != NULL) {
  393.     *parentPtr->abortPtr = 1;
  394.     }
  395.     parentPtr->abortPtr = &abort;
  396.     abort = 0;
  397.     Tk_Preserve((ClientData) parentPtr);
  398.  
  399.     /*
  400.      * Pass #1: scan all the children to figure out the total amount
  401.      * of space needed.  Two separate widths and heights are computed.
  402.      *
  403.      * "Width" and "height" compute the minimum parent size to meet
  404.      * the needs of each window in the direction "where there is
  405.      * flexibility".  For example, if a child is packed TOP, then
  406.      * y is the flexible direction:  the child's requested height
  407.      * will determine its size.  For this window x is the inflexible
  408.      * direction:  the window's width will be determined by the amount
  409.      * of space left in the parent's cavity, not by the window's
  410.      * requested width.  "Width" and "height" are needed in order to
  411.      * compute how much extra space there is, so that it can be divided
  412.      * among the windows that have the EXPAND flag.
  413.      *
  414.      * "MaxWidth" and "maxHeight" compute the minimum parent size to
  415.      * meet all the needs of every window in both directions, flexible
  416.      * or inflexible.  These values are needed to make geometry requests
  417.      * of the parent's parent.
  418.      */
  419.  
  420.     intBWidth = Tk_InternalBorderWidth(parentPtr->tkwin);
  421.     width = height = maxWidth = maxHeight = 2*intBWidth;
  422.     numExpX = numExpY = 0;
  423.     for (childPtr = parentPtr->childPtr; childPtr != NULL;
  424.         childPtr = childPtr->nextPtr) {
  425.     if ((childPtr->side == TOP) || (childPtr->side == BOTTOM)) {
  426.         tmp = Tk_ReqWidth(childPtr->tkwin) + childPtr->doubleBw
  427.             + childPtr->padX + width;
  428.         if (tmp > maxWidth) {
  429.         maxWidth = tmp;
  430.         }
  431.         height += Tk_ReqHeight(childPtr->tkwin) + childPtr->doubleBw
  432.             + childPtr->padY;
  433.         if (childPtr->flags & EXPAND) {
  434.         numExpY++;
  435.         }
  436.     } else {
  437.         tmp = Tk_ReqHeight(childPtr->tkwin) + childPtr->doubleBw
  438.             + childPtr->padY + height;
  439.         if (tmp > maxHeight) {
  440.         maxHeight = tmp;
  441.         }
  442.         width += Tk_ReqWidth(childPtr->tkwin) + childPtr->doubleBw
  443.             + childPtr->padX;
  444.         if (childPtr->flags & EXPAND) {
  445.         numExpX++;
  446.         }
  447.     }
  448.     }
  449.     if (width > maxWidth) {
  450.     maxWidth = width;
  451.     }
  452.     if (height > maxHeight) {
  453.     maxHeight = height;
  454.     }
  455.  
  456.     /*
  457.      * If the total amount of space needed in the parent window has
  458.      * changed, then notify the next geometry manager up and requeue
  459.      * ourselves to start again after the parent has had a chance to
  460.      * resize us.
  461.      */
  462.  
  463.     if ((maxWidth != Tk_ReqWidth(parentPtr->tkwin))
  464.         || (maxHeight != Tk_ReqHeight(parentPtr->tkwin))) {
  465.     Tk_GeometryRequest(parentPtr->tkwin, maxWidth, maxHeight);
  466.     parentPtr->flags |= REQUESTED_REPACK;
  467.     Tk_DoWhenIdle(ArrangePacking, (ClientData) parentPtr);
  468.     goto done;
  469.     }
  470.  
  471.     /*
  472.      * If there is spare space, figure out how much of it goes to
  473.      * each of the windows that is expandable.
  474.      */
  475.  
  476.     spareX = Tk_Width(parentPtr->tkwin) - width;
  477.     spareY = Tk_Height(parentPtr->tkwin) - height;
  478.     if ((spareX <= 0) || (numExpX == 0)) {
  479.     leftOverX = 0;
  480.     spareX = 0;
  481.     } else {
  482.     leftOverX = spareX % numExpX;
  483.     spareX /= numExpX;
  484.     }
  485.     if ((spareY <= 0) || (numExpY == 0)) {
  486.     leftOverY = spareY;
  487.     spareY = 0;
  488.     } else {
  489.     leftOverY = spareY % numExpY;
  490.     spareY /= numExpY;
  491.     }
  492.  
  493.     /*
  494.      * Pass #2: scan the children a second time assigning
  495.      * new sizes.  The "cavity" variables keep track of the
  496.      * unclaimed space in the cavity of the window;  this
  497.      * shrinks inward as we allocate windows around the
  498.      * edges.  The "frame" variables keep track of the space
  499.      * allocated to the current window and its frame.  The
  500.      * current window is then placed somewhere inside the
  501.      * frame, depending on anchorPoint.
  502.      */
  503.  
  504.     cavityX = cavityY = x = y = intBWidth;
  505.     cavityWidth = Tk_Width(parentPtr->tkwin) - 2*intBWidth;
  506.     cavityHeight = Tk_Height(parentPtr->tkwin) - 2*intBWidth;
  507.     for (childPtr = parentPtr->childPtr; childPtr != NULL;
  508.         childPtr = childPtr->nextPtr) {
  509.     if ((childPtr->side == TOP) || (childPtr->side == BOTTOM)) {
  510.         frameWidth = cavityWidth;
  511.         frameHeight = Tk_ReqHeight(childPtr->tkwin) + childPtr->padY
  512.             + childPtr->doubleBw;
  513.         if (childPtr->flags & EXPAND) {
  514.         frameHeight += spareY;
  515.         numExpY--;
  516.         if (numExpY == 0) {
  517.             frameHeight += leftOverY;
  518.         }
  519.         }
  520.         cavityHeight -= frameHeight;
  521.         if (cavityHeight < 0) {
  522.         frameHeight += cavityHeight;
  523.         cavityHeight = 0;
  524.         }
  525.         frameX = cavityX;
  526.         if (childPtr->side == TOP) {
  527.         frameY = cavityY;
  528.         cavityY += frameHeight;
  529.         } else {
  530.         frameY = cavityY + cavityHeight;
  531.         }
  532.     } else {
  533.         frameHeight = cavityHeight;
  534.         frameWidth = Tk_ReqWidth(childPtr->tkwin) + childPtr->padX
  535.             + childPtr->doubleBw;
  536.         if (childPtr->flags & EXPAND) {
  537.         frameWidth += spareX;
  538.         numExpX--;
  539.         if (numExpX == 0) {
  540.             frameWidth += leftOverX;
  541.         }
  542.         }
  543.         cavityWidth -= frameWidth;
  544.         if (cavityWidth < 0) {
  545.         frameWidth += cavityWidth;
  546.         cavityWidth = 0;
  547.         }
  548.         frameY = cavityY;
  549.         if (childPtr->side == LEFT) {
  550.         frameX = cavityX;
  551.         cavityX += frameWidth;
  552.         } else {
  553.         frameX = cavityX + cavityWidth;
  554.         }
  555.     }
  556.  
  557.     /*
  558.      * Now that we've got the size of the frame for the window,
  559.      * compute the window's actual size and location using the
  560.      * fill and frame factors.
  561.      */
  562.  
  563.     width = Tk_ReqWidth(childPtr->tkwin) + childPtr->doubleBw;
  564.     if ((childPtr->flags & FILLX) || (width > frameWidth)) {
  565.         width = frameWidth;
  566.     }
  567.     height = Tk_ReqHeight(childPtr->tkwin) + childPtr->doubleBw;
  568.     if ((childPtr->flags & FILLY) || (height > frameHeight)) {
  569.         height = frameHeight;
  570.     }
  571.     switch (childPtr->anchorPoint) {
  572.         case TK_ANCHOR_N:
  573.         x = frameX + (frameWidth - width)/2;
  574.         y = frameY;
  575.         break;
  576.         case TK_ANCHOR_NE:
  577.         x = frameX + frameWidth - width;
  578.         y = frameY;
  579.         break;
  580.         case TK_ANCHOR_E:
  581.         x = frameX + frameWidth - width;
  582.         y = frameY + (frameHeight - height)/2;
  583.         break;
  584.         case TK_ANCHOR_SE:
  585.         x = frameX + frameWidth - width;
  586.         y = frameY + frameHeight - height;
  587.         break;
  588.         case TK_ANCHOR_S:
  589.         x = frameX + (frameWidth - width)/2;
  590.         y = frameY + frameHeight - height;
  591.         break;
  592.         case TK_ANCHOR_SW:
  593.         x = frameX;
  594.         y = frameY + frameHeight - height;
  595.         break;
  596.         case TK_ANCHOR_W:
  597.         x = frameX;
  598.         y = frameY + (frameHeight - height)/2;
  599.         break;
  600.         case TK_ANCHOR_NW:
  601.         x = frameX;
  602.         y = frameY;
  603.         break;
  604.         case TK_ANCHOR_CENTER:
  605.         x = frameX + (frameWidth - width)/2;
  606.         y = frameY + (frameHeight - height)/2;
  607.         break;
  608.         default:
  609.         panic("bad frame factor in ArrangePacking");
  610.     }
  611.     width -= childPtr->doubleBw;
  612.     height -= childPtr->doubleBw;
  613.  
  614.     /*
  615.      * If the window is too small to be interesting then
  616.      * unmap it.  Otherwise configure it and then make sure
  617.      * it's mapped.
  618.      */
  619.  
  620.     if ((width <= 0) || (height <= 0)) {
  621.         Tk_UnmapWindow(childPtr->tkwin);
  622.     } else {
  623.         if ((x != Tk_X(childPtr->tkwin))
  624.             || (y != Tk_Y(childPtr->tkwin))
  625.             || (width != Tk_Width(childPtr->tkwin))
  626.             || (height != Tk_Height(childPtr->tkwin))) {
  627.         Tk_MoveResizeWindow(childPtr->tkwin, x, y,
  628.             (unsigned int) width, (unsigned int) height);
  629.         }
  630.         if (abort) {
  631.         goto done;
  632.         }
  633.         Tk_MapWindow(childPtr->tkwin);
  634.     }
  635.  
  636.     /*
  637.      * Changes to the window's structure could cause almost anything
  638.      * to happen, including deleting the parent or child.  If this
  639.      * happens, we'll be told to abort.
  640.      */
  641.  
  642.     if (abort) {
  643.         goto done;
  644.     }
  645.     }
  646.  
  647.     done:
  648.     parentPtr->abortPtr = NULL;
  649.     Tk_Release((ClientData) parentPtr);
  650. }
  651.  
  652. /*
  653.  *--------------------------------------------------------------
  654.  *
  655.  * GetPacker --
  656.  *
  657.  *    This internal procedure is used to locate a Packer
  658.  *    structure for a given window, creating one if one
  659.  *    doesn't exist already.
  660.  *
  661.  * Results:
  662.  *    The return value is a pointer to the Packer structure
  663.  *    corresponding to tkwin.
  664.  *
  665.  * Side effects:
  666.  *    A new packer structure may be created.  If so, then
  667.  *    a callback is set up to clean things up when the
  668.  *    window is deleted.
  669.  *
  670.  *--------------------------------------------------------------
  671.  */
  672.  
  673. static Packer *
  674. GetPacker(tkwin)
  675.     Tk_Window tkwin;        /* Token for window for which
  676.                  * packer structure is desired. */
  677. {
  678.     register Packer *packPtr;
  679.     Tcl_HashEntry *hPtr;
  680.     int new;
  681.  
  682.     if (!initialized) {
  683.     initialized = 1;
  684.     Tcl_InitHashTable(&packerHashTable, TCL_ONE_WORD_KEYS);
  685.     }
  686.  
  687.     /*
  688.      * See if there's already packer for this window.  If not,
  689.      * then create a new one.
  690.      */
  691.  
  692.     hPtr = Tcl_CreateHashEntry(&packerHashTable, (char *) tkwin, &new);
  693.     if (!new) {
  694.     return (Packer *) Tcl_GetHashValue(hPtr);
  695.     }
  696.     packPtr = (Packer *) ckalloc(sizeof(Packer));
  697.     packPtr->tkwin = tkwin;
  698.     packPtr->parentPtr = NULL;
  699.     packPtr->nextPtr = NULL;
  700.     packPtr->childPtr = NULL;
  701.     packPtr->side = TOP;
  702.     packPtr->anchorPoint = TK_ANCHOR_CENTER;
  703.     packPtr->padX = packPtr->padY = 0;
  704.     packPtr->doubleBw = 2*Tk_Changes(tkwin)->border_width;
  705.     packPtr->abortPtr = NULL;
  706.     packPtr->flags = 0;
  707.     Tcl_SetHashValue(hPtr, packPtr);
  708.     Tk_CreateEventHandler(tkwin, StructureNotifyMask,
  709.         PackStructureProc, (ClientData) packPtr);
  710.     return packPtr;
  711. }
  712.  
  713. /*
  714.  *--------------------------------------------------------------
  715.  *
  716.  * PackAfter --
  717.  *
  718.  *    This procedure does most of the real work of adding
  719.  *    one or more windows into the packing order for its parent.
  720.  *
  721.  * Results:
  722.  *    A standard Tcl return value.
  723.  *
  724.  * Side effects:
  725.  *    The geometry of the specified windows may change, both now and
  726.  *    again in the future.
  727.  *
  728.  *--------------------------------------------------------------
  729.  */
  730.  
  731. static int
  732. PackAfter(interp, prevPtr, parentPtr, argc, argv)
  733.     Tcl_Interp *interp;        /* Interpreter for error reporting. */
  734.     Packer *prevPtr;        /* Pack windows in argv just after this
  735.                  * window;  NULL means pack as first
  736.                  * child of parentPtr. */
  737.     Packer *parentPtr;        /* Parent in which to pack windows. */
  738.     int argc;            /* Number of elements in argv. */
  739.     char **argv;        /* Array of lists, each containing 2
  740.                  * elements:  window name and side
  741.                  * against which to pack. */
  742. {
  743.     register Packer *packPtr;
  744.     Tk_Window tkwin;
  745.     int length, optionCount;
  746.     char **options;
  747.     int index;
  748.     char c;
  749.  
  750.     /*
  751.      * Iterate over all of the window specifiers, each consisting of
  752.      * two arguments.  The first argument contains the window name and
  753.      * the additional arguments contain options such as "top" or
  754.      * "padx 20".
  755.      */
  756.  
  757.     for ( ; argc > 0; argc -= 2, argv += 2, prevPtr = packPtr) {
  758.     if (argc < 2) {
  759.         Tcl_AppendResult(interp, "wrong # args: window \"",
  760.             argv[0], "\" should be followed by options",
  761.             (char *) NULL);
  762.         return TCL_ERROR;
  763.     }
  764.  
  765.     /*
  766.      * Find the packer for the window to be packed, and make sure
  767.      * that the window in which it will be packed is its parent.
  768.      */
  769.  
  770.     tkwin = Tk_NameToWindow(interp, argv[0], parentPtr->tkwin);
  771.     if (tkwin == NULL) {
  772.         return TCL_ERROR;
  773.     }
  774.     if (Tk_Parent(tkwin) != parentPtr->tkwin) {
  775.         Tcl_AppendResult(interp, "tried to pack \"",
  776.             argv[0], "\" in window that isn't its parent",
  777.             (char *) NULL);
  778.         return TCL_ERROR;
  779.     }
  780.     packPtr = GetPacker(tkwin);
  781.  
  782.     /*
  783.      * Process options for this window.
  784.      */
  785.  
  786.     if (Tcl_SplitList(interp, argv[1], &optionCount, &options) != TCL_OK) {
  787.         return TCL_ERROR;
  788.     }
  789.     packPtr->side = TOP;
  790.     packPtr->anchorPoint = TK_ANCHOR_CENTER;
  791.     packPtr->padX = packPtr->padY = 0;
  792.     packPtr->flags &= ~(FILLX|FILLY|EXPAND);
  793.     for (index = 0 ; index < optionCount; index++) {
  794.         char *curOpt = options[index];
  795.  
  796.         c = curOpt[0];
  797.         length = strlen(curOpt);
  798.  
  799.         if ((c == 't')
  800.             && (strncmp(curOpt, "top", length)) == 0) {
  801.         packPtr->side = TOP;
  802.         } else if ((c == 'b')
  803.             && (strncmp(curOpt, "bottom", length)) == 0) {
  804.         packPtr->side = BOTTOM;
  805.         } else if ((c == 'l')
  806.             && (strncmp(curOpt, "left", length)) == 0) {
  807.         packPtr->side = LEFT;
  808.         } else if ((c == 'r')
  809.             && (strncmp(curOpt, "right", length)) == 0) {
  810.         packPtr->side = RIGHT;
  811.         } else if ((c == 'e')
  812.             && (strncmp(curOpt, "expand", length)) == 0) {
  813.         packPtr->flags |= EXPAND;
  814.         } else if ((c == 'f')
  815.             && (strcmp(curOpt, "fill")) == 0) {
  816.         packPtr->flags |= FILLX|FILLY;
  817.         } else if ((length == 5) && (strcmp(curOpt, "fillx")) == 0) {
  818.         packPtr->flags |= FILLX;
  819.         } else if ((length == 5) && (strcmp(curOpt, "filly")) == 0) {
  820.         packPtr->flags |= FILLY;
  821.         } else if ((c == 'p') && (strcmp(curOpt, "padx")) == 0) {
  822.         if (optionCount < (index+2)) {
  823.             missingPad:
  824.             Tcl_AppendResult(interp, "wrong # args: \"", curOpt,
  825.                 "\" option must be followed by count",
  826.                 (char *) NULL);
  827.             goto error;
  828.         }
  829.         if ((Tcl_GetInt(interp, options[index+1], &packPtr->padX)
  830.             != TCL_OK) || (packPtr->padX < 0)) {
  831.             badPad:
  832.             Tcl_AppendResult(interp, "bad pad value \"",
  833.                 options[index+1], "\":  must be positive integer",
  834.                 (char *) NULL);
  835.             goto error;
  836.         }
  837.         index++;
  838.         } else if ((c == 'p') && (strcmp(curOpt, "pady")) == 0) {
  839.         if (optionCount < (index+2)) {
  840.             goto missingPad;
  841.         }
  842.         if ((Tcl_GetInt(interp, options[index+1], &packPtr->padY)
  843.             != TCL_OK) || (packPtr->padY < 0)) {
  844.             goto badPad;
  845.         }
  846.         index++;
  847.         } else if ((c == 'f') && (length > 1)
  848.             && (strncmp(curOpt, "frame", length) == 0)) {
  849.         if (optionCount < (index+2)) {
  850.             Tcl_AppendResult(interp, "wrong # args: \"frame\" ",
  851.                 "option must be followed by anchor point",
  852.                 (char *) NULL);
  853.             goto error;
  854.         }
  855.         if (Tk_GetAnchor(interp, options[index+1],
  856.             &packPtr->anchorPoint) != TCL_OK) {
  857.             goto error;
  858.         }
  859.         index++;
  860.         } else {
  861.         Tcl_AppendResult(interp, "bad option \"", curOpt,
  862.             "\":  should be top, bottom, left, right, ",
  863.             "expand, fill, fillx, filly, padx, pady, or frame",
  864.             (char *) NULL);
  865.         goto error;
  866.         }
  867.     }
  868.  
  869.     if (packPtr != prevPtr) {
  870.  
  871.         /*
  872.          * Unpack this window if it's currently packed.
  873.          */
  874.     
  875.         if (packPtr->parentPtr != NULL) {
  876.         Unlink(packPtr);
  877.         }
  878.     
  879.         /*
  880.          * Add the window in the correct place in its parent's
  881.          * packing order, then make sure that the window is
  882.          * managed by us.
  883.          */
  884.  
  885.         packPtr->parentPtr = parentPtr;
  886.         if (prevPtr == NULL) {
  887.         packPtr->nextPtr = parentPtr->childPtr;
  888.         parentPtr->childPtr = packPtr;
  889.         } else {
  890.         packPtr->nextPtr = prevPtr->nextPtr;
  891.         prevPtr->nextPtr = packPtr;
  892.         }
  893.         Tk_ManageGeometry(tkwin, PackReqProc, (ClientData) packPtr);
  894.     }
  895.     ckfree((char *) options);
  896.     }
  897.  
  898.     /*
  899.      * Arrange for the parent to be re-packed at the first
  900.      * idle moment.
  901.      */
  902.  
  903.     if (parentPtr->abortPtr != NULL) {
  904.     *parentPtr->abortPtr = 1;
  905.     }
  906.     if (!(parentPtr->flags & REQUESTED_REPACK)) {
  907.     parentPtr->flags |= REQUESTED_REPACK;
  908.     Tk_DoWhenIdle(ArrangePacking, (ClientData) parentPtr);
  909.     }
  910.     return TCL_OK;
  911.  
  912.     error:
  913.     ckfree((char *) options);
  914.     return TCL_ERROR;
  915. }
  916.  
  917. /*
  918.  *----------------------------------------------------------------------
  919.  *
  920.  * Unlink --
  921.  *
  922.  *    Remove a packer from its parent's list of children.
  923.  *
  924.  * Results:
  925.  *    None.
  926.  *
  927.  * Side effects:
  928.  *    The parent will be scheduled for repacking.
  929.  *
  930.  *----------------------------------------------------------------------
  931.  */
  932.  
  933. static void
  934. Unlink(packPtr)
  935.     register Packer *packPtr;        /* Window to unlink. */
  936. {
  937.     register Packer *parentPtr, *packPtr2;
  938.  
  939.     parentPtr = packPtr->parentPtr;
  940.     if (parentPtr == NULL) {
  941.     return;
  942.     }
  943.     if (parentPtr->childPtr == packPtr) {
  944.     parentPtr->childPtr = packPtr->nextPtr;
  945.     } else {
  946.     for (packPtr2 = parentPtr->childPtr; ; packPtr2 = packPtr2->nextPtr) {
  947.         if (packPtr2 == NULL) {
  948.         panic("Unlink couldn't find previous window");
  949.         }
  950.         if (packPtr2->nextPtr == packPtr) {
  951.         packPtr2->nextPtr = packPtr->nextPtr;
  952.         break;
  953.         }
  954.     }
  955.     }
  956.     if (!(parentPtr->flags & REQUESTED_REPACK)) {
  957.     parentPtr->flags |= REQUESTED_REPACK;
  958.     Tk_DoWhenIdle(ArrangePacking, (ClientData) parentPtr);
  959.     }
  960.     if (parentPtr->abortPtr != NULL) {
  961.     *parentPtr->abortPtr = 1;
  962.     }
  963.  
  964.     packPtr->parentPtr = NULL;
  965. }
  966.  
  967. /*
  968.  *----------------------------------------------------------------------
  969.  *
  970.  * DestroyPacker --
  971.  *
  972.  *    This procedure is invoked by Tk_EventuallyFree or Tk_Release
  973.  *    to clean up the internal structure of a packer at a safe time
  974.  *    (when no-one is using it anymore).
  975.  *
  976.  * Results:
  977.  *    None.
  978.  *
  979.  * Side effects:
  980.  *    Everything associated with the packer is freed up.
  981.  *
  982.  *----------------------------------------------------------------------
  983.  */
  984.  
  985. static void
  986. DestroyPacker(clientData)
  987.     ClientData clientData;        /* Info about packed window that
  988.                      * is now dead. */
  989. {
  990.     register Packer *packPtr = (Packer *) clientData;
  991.     ckfree((char *) packPtr);
  992. }
  993.  
  994. /*
  995.  *----------------------------------------------------------------------
  996.  *
  997.  * PackStructureProc --
  998.  *
  999.  *    This procedure is invoked by the Tk event dispatcher in response
  1000.  *    to StructureNotify events.
  1001.  *
  1002.  * Results:
  1003.  *    None.
  1004.  *
  1005.  * Side effects:
  1006.  *    If a window was just deleted, clean up all its packer-related
  1007.  *    information.  If it was just resized, repack its children, if
  1008.  *    any.
  1009.  *
  1010.  *----------------------------------------------------------------------
  1011.  */
  1012.  
  1013. static void
  1014. PackStructureProc(clientData, eventPtr)
  1015.     ClientData clientData;        /* Our information about window
  1016.                      * referred to by eventPtr. */
  1017.     XEvent *eventPtr;            /* Describes what just happened. */
  1018. {
  1019.     register Packer *packPtr = (Packer *) clientData;
  1020.     if (eventPtr->type == ConfigureNotify) {
  1021.     if ((packPtr->childPtr != NULL)
  1022.         && !(packPtr->flags & REQUESTED_REPACK)) {
  1023.         packPtr->flags |= REQUESTED_REPACK;
  1024.         Tk_DoWhenIdle(ArrangePacking, (ClientData) packPtr);
  1025.     }
  1026.     if (packPtr->doubleBw != 2*Tk_Changes(packPtr->tkwin)->border_width) {
  1027.         if ((packPtr->parentPtr != NULL)
  1028.             && !(packPtr->parentPtr->flags & REQUESTED_REPACK)) {
  1029.         packPtr->doubleBw = 2*Tk_Changes(packPtr->tkwin)->border_width;
  1030.         packPtr->parentPtr->flags |= REQUESTED_REPACK;
  1031.         Tk_DoWhenIdle(ArrangePacking, (ClientData) packPtr->parentPtr);
  1032.         }
  1033.     }
  1034.     } else if (eventPtr->type == DestroyNotify) {
  1035.     register Packer *packPtr2;
  1036.  
  1037.     if (packPtr->parentPtr != NULL) {
  1038.         Unlink(packPtr);
  1039.     }
  1040.     for (packPtr2 = packPtr->childPtr; packPtr2 != NULL;
  1041.         packPtr2 = packPtr2->nextPtr) {
  1042.         packPtr2->parentPtr = NULL;
  1043.         packPtr2->nextPtr = NULL;
  1044.     }
  1045.     Tcl_DeleteHashEntry(Tcl_FindHashEntry(&packerHashTable,
  1046.         (char *) packPtr->tkwin));
  1047.     if (packPtr->flags & REQUESTED_REPACK) {
  1048.         Tk_CancelIdleCall(ArrangePacking, (ClientData) packPtr);
  1049.     }
  1050.     packPtr->tkwin = NULL;
  1051.     Tk_EventuallyFree((ClientData) packPtr, DestroyPacker);
  1052.     }
  1053. }
  1054.